//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include <wx/statline.h>
#include "../common/MarkupSTL.h"
#include "xmlguipanel.h"

enum cWinID { cIDSLoadAtStartUP=19000, cIDStart };

/*
doc.AddChildElem( XMLUI_UI_TAG );
	doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"startup_load" );
	doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
	doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Apply at Startup") ).utf8_str()	);
*/

CXMLGUIPanel::CXMLGUIPanel(wxWindow* parent, IGUIXMLExecutor* pEx ):
	 wxVScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL),
	 m_nFlags(wxALL|wxGROW), m_nBorder(3), m_pEx(pEx)
{
	wxBoxSizer* pGlobal = new wxBoxSizer(wxVERTICAL);

	m_pSizer = new wxFlexGridSizer(3, 3, 0, 0);

	pGlobal->Add(m_pSizer, 1 );

	m_pStartUP = new wxCheckBox(this, cIDSLoadAtStartUP, _("Apply at Startup") );
	Connect( cIDSLoadAtStartUP, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CXMLGUIPanel::OnChanged));
	pGlobal->Add(m_pStartUP, 0, wxALL,  m_nBorder);

	SetSizer(pGlobal);
	pGlobal->FitInside(this);
	//SetScrollRate(5,5);
}

CXMLGUIPanel::~CXMLGUIPanel(void)
{

}

void CXMLGUIPanel::ShowAdvanced(bool bShow, const std::string& strCaps)
{
	if ( bShow )
	{
		if ( m_pSizer->GetChildren().empty() ||
			 m_strCapsXML!=strCaps )
		{
			m_pSizer->Clear(true);

			m_strCapsXML = strCaps;

			BuildUI();
			LoadData();
		}
	}

	wxSize sz = m_pSizer->CalcMin();
	m_pSizer->RecalcSizes();

	SetMinSize( sz );
	SetRowCount( sz.y/10);
}


void CXMLGUIPanel::BuildUI()
{
	m_lstGUIItems.erase( m_lstGUIItems.begin(), m_lstGUIItems.end() );
	m_bDirty = false;

	m_nId = cIDStart;

	CMarkupSTL doc;
	
	if ( doc.SetDoc(m_strCapsXML.c_str()) )
	{
		while( doc.FindChildElem() )
		{
			std::string strEl = doc.GetChildTagName();

			if ( strEl == XMLUI_BLOCK_TAG )
			{
				RenderBlock(doc);
			}
			else
			{
				RenderItem(doc, GetSizer(), GetSizer());
			}
		}
	}
}

void CXMLGUIPanel::RenderBlock(CMarkupSTL& doc)
{
	wxString strBlockName = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_BLOCK_NAME_ATR).c_str() );

	//Startup
	wxStaticBoxSizer* pBox = new wxStaticBoxSizer( new
		wxStaticBox(this, wxID_ANY, strBlockName ), wxVERTICAL);

	m_pSizer->Add( pBox, 1, m_nFlags|wxALIGN_CENTER, m_nBorder);

	wxFlexGridSizer* pInternal = new wxFlexGridSizer(2, 0, 0);

	pBox->Add( pInternal, 1, wxALL|wxALIGN_CENTER, m_nBorder);

	if ( doc.IntoElem() )
	{
		while( doc.FindChildElem() )
		{
			RenderItem(doc, pInternal, pBox);
		}

		doc.OutOfElem();
	}
}

void CXMLGUIPanel::RenderItem(class CMarkupSTL& doc, wxSizer* pSizer, wxSizer* pSizerParent)
{
	std::string strEl = doc.GetChildTagName();

	if ( strEl==XMLUI_UI_TAG )
	{
		std::string strGroupID = doc.GetAttrib(XMLUI_ELEMENT_BLOCK_ID_ATTR);

		std::string strType = doc.GetChildAttrib(XMLUI_TYPE_ATR);
		std::string strID = doc.GetChildAttrib(XMLUI_ELEMENT_ID_ATTR);
		std::string strValFrom = doc.GetChildAttrib(XMLUI_VAL_ATTR);
		std::string strCMD = doc.GetChildAttrib(XMLUI_VAL_TO_ATTR);
		std::string strApplyTo = doc.GetChildAttrib(XMLUI_UI_ATTR_BTN_APP_BLOCK);
		std::string	strCMDOn = doc.GetChildAttrib(XMLUI_VAL_TO_ON_ATTR);
		std::string	strCMDOff = doc.GetChildAttrib(XMLUI_VAL_TO_OFF_ATTR);

		std::string	strInputType = doc.GetChildAttrib(XMLUI_UI_ATTR_INPUT_TYPE);
		std::string	strInputFrom = doc.GetChildAttrib(XMLUI_UI_ATTR_INPUT_FROM);
		std::string	strInputTo = doc.GetChildAttrib(XMLUI_UI_ATTR_INPUT_TO);

		std::string strLookUpVal = doc.GetChildAttrib(XMLUI_ELEMENT_LOOKUP_ID_ATTR);

		if (strLookUpVal.empty())
		{
			strLookUpVal = strID;
		}

		wxString strDesc = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_MODE_DESC_ATTR).c_str() );

		wxWindow* p(NULL);

		if ( strType ==XMLUI_UI_ATTR_RADIO )
		{
			++m_nId;
			p=RenderRadioBox(doc, pSizer, m_nId);

			Connect( m_nId, wxEVT_COMMAND_RADIOBUTTON_SELECTED,
				wxCommandEventHandler(CXMLGUIPanel::OnChanged));
		}
		else
		if ( strType ==XMLUI_UI_ATTR_CHECK )
		{
			++m_nId;
			p=RenderCheckBox(doc, pSizer, m_nId);

			Connect( m_nId, wxEVT_COMMAND_CHECKBOX_CLICKED,
				wxCommandEventHandler(CXMLGUIPanel::OnChanged));
		}
		else
		if ( strType ==XMLUI_UI_ATTR_BUTTON )
		{
			++m_nId;
			p=RenderButton(doc, pSizerParent, m_nId);

			Connect( m_nId, wxEVT_COMMAND_BUTTON_CLICKED,
				wxCommandEventHandler(CXMLGUIPanel::OnBtnClicked));
		}
		else
		if ( strType ==XMLUI_UI_ATTR_INPUT )
		{
			++m_nId;
			p=RenderInput(doc, pSizer, m_nId);

			Connect(m_nId, wxEVT_COMMAND_TEXT_UPDATED,
				wxCommandEventHandler(CXMLGUIPanel::OnChanged));
		}
		else
		if ( strType == XMLUI_UI_ATTR_LINE )
		{
			RenderLine(doc, pSizer);
		}

		if ( p )
		{
			p->SetToolTip( strDesc );

			GUIITEM item;
			item.strGroupID = strGroupID;
			item.strType= strType;
			item.pWND	= p;
			item.strID  = strID;
			item.strValueLookUpName = strValFrom;
			item.strCMD = strCMD;
			item.strApplyTo = strApplyTo;
			item.strCMDOn = strCMDOn;
			item.strCMDOff = strCMDOff;
			item.strInputType = strInputType;
			item.strInputFrom = strInputFrom;
			item.strInputTo = strInputTo;
			item.strLookUpVal = strLookUpVal;

			m_lstGUIItems.push_back(item);
		}
	}
	else
	{
		assert(0);
	}
}

class wxWindow* CXMLGUIPanel::RenderRadioBox(class CMarkupSTL& doc, wxSizer* pSizer, int nID)
{
	wxString strLabel = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_MODE_NAME_ATTR).c_str() );
	wxRadioButton* p = new wxRadioButton(this, nID, strLabel);
	pSizer->Add( p, 0, m_nFlags, m_nBorder);
	return p;
}

class wxWindow* CXMLGUIPanel::RenderCheckBox(class CMarkupSTL& doc, wxSizer* pSizer, int nID)
{
	wxString strLabel = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_MODE_NAME_ATTR).c_str() );
	wxCheckBox* p = new wxCheckBox(this, nID, strLabel);
	pSizer->Add( p, 0, m_nFlags, m_nBorder);
	return p;
}

class wxWindow* CXMLGUIPanel::RenderButton(class CMarkupSTL& doc, wxSizer* pSizer, int nID)
{
	wxString strLabel = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_MODE_NAME_ATTR).c_str() );
	wxButton* p = new wxButton(this, nID, strLabel, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
	pSizer->Add( p, 0, m_nFlags|wxALIGN_CENTER, m_nBorder);
	return p;
}

class wxWindow* CXMLGUIPanel::RenderInput(class CMarkupSTL& doc, wxSizer* pSizer, int nID)
{
	wxString strLabel = wxString::FromUTF8( doc.GetChildAttrib(XMLUI_MODE_NAME_ATTR).c_str() );

	wxBoxSizer* pBox = new wxBoxSizer(wxHORIZONTAL);
	wxStaticText* pLabel = new wxStaticText(this, wxID_ANY, strLabel);

	pBox->Add(pLabel, 0, wxALIGN_CENTER_VERTICAL);
	wxTextCtrl* p = new wxTextCtrl(this, nID, wxT(""), wxDefaultPosition, wxSize(40,20),0,
		wxTextValidator(wxFILTER_NUMERIC, NULL) );

	pBox->Add(p, 0, wxLEFT|wxALIGN_CENTER_VERTICAL,5);
	pSizer->Add( pBox, 0, m_nFlags, m_nBorder);

	return p;
}

void CXMLGUIPanel::RenderLine(class CMarkupSTL& doc, wxSizer* pSizer)
{
	//Line
	pSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxSize(10,2), wxLI_HORIZONTAL), 0, wxALL|wxEXPAND, 10);

}

void CXMLGUIPanel::SetStatXML(const std::string& strXML)
{
	if ( m_lstGUIItems.empty() )
	{
		BuildUI();
	}

	if ( !m_lstGUIItems.empty() )
	{
		if ( m_strStatXML!=strXML &&
			 !m_bDirty )
		{
			m_strStatXML=strXML;

			UpdateUIVals();
		}
	}
}

void CXMLGUIPanel::UpdateUIVals()
{
	for( GUIITEMS::iterator i=m_lstGUIItems.begin();
		i!=m_lstGUIItems.end();++i )
	{
		GUIITEM& item = *i;

		if ( item.strValueLookUpName.empty() )
		{
			//LoadFromSavedAction();
		}
		else
		{
			std::string strVal = LookUpValueFromStatXML(item.strValueLookUpName);

			if ( !strVal.empty() )
			{
				ApplyValue(item, strVal);
			}
		}
	}
}

void CXMLGUIPanel::ApplyControlValue( GUIITEM& item,  const std::string& strVal)
{
	if ( item.strType==XMLUI_UI_ATTR_RADIO )
	{
		bool bVal = strVal=="true";
		((wxRadioButton*)item.pWND)->SetValue(bVal);
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_CHECK )
	{
		bool bVal = strVal=="true";
		((wxCheckBox*)item.pWND)->SetValue(bVal);
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_INPUT )
	{
		((wxTextCtrl*)item.pWND)->SetValue( wxString::FromAscii( strVal.c_str()) );
	}
}

void CXMLGUIPanel::ApplyValue( GUIITEM& item,  const std::string& strVal)
{
	if ( item.strType==XMLUI_UI_ATTR_RADIO )
	{
		if ( item.strLookUpVal==strVal  )
		{
			((wxRadioButton*)item.pWND)->SetValue(true);
		}
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_CHECK )
	{
		bool bVal = strVal=="true";
		bVal|= strVal=="ON";
		bVal|= strVal=="1";
		bVal|=  0 == strVal.find( item.strLookUpVal );

		((wxCheckBox*)item.pWND)->SetValue(bVal);
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_INPUT )
	{
		((wxTextCtrl*)item.pWND)->SetValue( wxString::FromAscii( strVal.c_str()) );
	}
}

std::string CXMLGUIPanel::LookUpValueFromStatXML(const std::string& strName) const
{
	std::string strRes;

	CMarkupSTL	doc;

	if ( doc.SetDoc( m_strStatXML.c_str() ) )
	{
		if ( doc.FindChildElem(ADSL_STAT_TAG) )
		{
			strRes = doc.GetChildAttrib( strName.c_str() );
		}
	}

	return strRes;
}

bool CXMLGUIPanel::LookUpItem(class wxWindow* pWND, GUIITEM& item ) const
{
	bool bRes(false);

	for(GUIITEMS::const_iterator i=m_lstGUIItems.begin();
		i!=m_lstGUIItems.end();++i)
	{
		const GUIITEM& refItem = *i;

		if ( refItem.pWND == pWND )
		{
			item = refItem;
			bRes = true;
			break;
		}
	}

	return bRes;
}

bool CXMLGUIPanel::LookUpItem(const std::string& strID, GUIITEM& item ) const
{
	bool bRes(false);

	for(GUIITEMS::const_iterator i=m_lstGUIItems.begin();
		i!=m_lstGUIItems.end();++i)
	{
		const GUIITEM& refItem = *i;

		if ( refItem.strID == strID )
		{
			item = refItem;
			bRes = true;
			break;
		}
	}

	return bRes;

}

std::string	CXMLGUIPanel::BuildItemVal(const GUIITEM& item) const
{
	std::string strRes;

	if ( item.strType==XMLUI_UI_ATTR_RADIO )
	{
		if ( ((wxRadioButton*)item.pWND)->GetValue() )
		{
			strRes = "true";
		}
		else
		{
			strRes = "false";
		}
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_CHECK )
	{
		if ( ((wxCheckBox*)item.pWND)->GetValue() ) 
		{
			strRes = "true";
		}
		else
		{
			strRes = "false";
		}
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_INPUT )
	{
		wxString str = ((wxTextCtrl*)item.pWND)->GetValue();
		strRes  = (const char*)str.ToAscii();
	}

	return strRes;
}

std::string CXMLGUIPanel::BuildItemAction(const GUIITEM& item) const
{
	std::string strRes;

	if ( item.strType==XMLUI_UI_ATTR_RADIO )
	{
		if ( ((wxRadioButton*)item.pWND)->GetValue() )
		{
			char buf[256];
			sprintf( buf, item.strCMD.c_str(), item.strID.c_str() );
			strRes = buf;
		}
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_CHECK )
	{
		bool bVal = ((wxCheckBox*)item.pWND)->GetValue();

		if ( bVal )
		{
			strRes = item.strCMDOn;
		}
		else
		{
			strRes = item.strCMDOff;
		}

		if ( strRes.empty() && bVal )
		{
			char buf[256];
			sprintf( buf, item.strCMD.c_str(), item.strID.c_str() );
			strRes = buf;
		}
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_BUTTON )
	{
		strRes = item.strCMD;
	}
	else
	if ( item.strType ==XMLUI_UI_ATTR_INPUT )
	{
		wxString str = ((wxTextCtrl*)item.pWND)->GetValue();

		if ( !str.empty() )
		{
			long val(0);
			str.ToLong(&val);

			if (item.strInputType == XMLUI_UI_ATTR_INPUT_TYPE_D )
			{
				char buf[256];
				sprintf( buf, item.strCMD.c_str(), val );
				strRes = buf;
			}
			else if (item.strInputType == XMLUI_UI_ATTR_INPUT_TYPE_S )
			{
				char buf[256];
				sprintf( buf, item.strCMD.c_str(), (const char*)str.ToAscii() );
				strRes = buf;
			}
		}
	}

	return strRes;
}

std::string	CXMLGUIPanel::BuildItemGroupXML(const GUIITEM& item) const
{
	CMarkupSTL	doc;
	doc.AddElem( XMLGUI_EXEC_EL );
	doc.AddAttrib( XMLGUI_EXEC_ATTR_STARTUP, m_pStartUP->GetValue()?"true":"false" );

	bool bHasData(false);

	for( GUIITEMS::const_iterator i=m_lstGUIItems.begin();
		i!=m_lstGUIItems.end();++i )
	{
		const GUIITEM& iEl = *i;

		if ( iEl.strType ==XMLUI_UI_ATTR_BUTTON )
		{
			continue;
		}

		if ( item.strApplyTo=="all" || 
			 iEl.strGroupID == item.strApplyTo )
		{
			std::string  strAction = BuildItemAction(iEl);

			if ( !strAction.empty() )
			{
				bHasData = true;

				std::string strVal = BuildItemVal(iEl);

				doc.AddChildElem( XMLGUI_EXEC_ITEM_EL, strAction.c_str());
				doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR, iEl.strID.c_str() );
				doc.AddChildAttrib( XMLUI_VAL_SET_ATTR, strVal.c_str() );
			}
		}
	}

	return bHasData?doc.GetDoc():"";
}

std::string	CXMLGUIPanel::BuildItemActionXML(const GUIITEM& item) const
{
	std::string strRes;
	std::string strAction = BuildItemAction(item);

	if ( !strAction.empty() )
	{
		std::string strVal = BuildItemVal(item);

		CMarkupSTL	doc;
		doc.AddElem( XMLGUI_EXEC_EL );
		doc.AddChildElem( XMLGUI_EXEC_ITEM_EL, strAction.c_str());
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR, item.strID.c_str() );
		doc.AddChildAttrib( XMLUI_VAL_SET_ATTR, strVal.c_str() );
		strRes = doc.GetDoc();
	}

	return strRes;
}

////////////////////////////////////////////////////////////////////////////////////////
void CXMLGUIPanel::OnBtnClicked(wxCommandEvent& ev)
{
	GUIITEM item;
	std::string str;

	if ( LookUpItem((wxWindow*)ev.GetEventObject(), item) )
	{
		assert( item.strType == XMLUI_UI_ATTR_BUTTON );

		if ( item.strApplyTo.empty() )
		{
			str = BuildItemActionXML(item);
		}
		else
		{
			str = BuildItemGroupXML(item);

			if ( !str.empty() )
			{
				m_pEx->SaveExecuteXML(str);
			}
		}

		if ( !str.empty() )
		{
			m_pEx->ExecuteXML(str);
			m_bDirty = false;
		}
	}
}

void CXMLGUIPanel::OnChanged(wxCommandEvent& ev)
{
	m_bDirty = true;
	SaveData();
}

void CXMLGUIPanel::LoadData()
{
	std::string str = m_pEx->LoadExecuteXML();

	CMarkupSTL	doc;

	if ( doc.SetDoc(str.c_str()) )
	{
		doc.FindElem();

		if ( doc.GetAttrib(XMLGUI_EXEC_ATTR_STARTUP)=="true" )
		{
			m_pStartUP->SetValue( true );
		}
		else
		{
			m_pStartUP->SetValue( false );
		}

		while( doc.FindChildElem(XMLGUI_EXEC_ITEM_EL) )
		{
			std::string strID = doc.GetChildAttrib(XMLUI_ELEMENT_ID_ATTR);
			std::string strVal = doc.GetChildAttrib(XMLUI_VAL_SET_ATTR);

			GUIITEM item;

			if ( LookUpItem(strID, item) )
			{
				ApplyControlValue(item, strVal);
			}
		}
	}
}

void CXMLGUIPanel::SaveData()
{
	GUIITEM item;
	item.strApplyTo="all";
	std::string str = BuildItemGroupXML(item);
	m_pEx->SaveExecuteXML( str );
}